#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
struct Goods
{
	string _name;//名字
	double _price;//价格
	int _enaluate;//评价
	//...
	Goods(const char* str, double price, int enaluate)
		:_name(str)
		,_price(price)
		, _enaluate(enaluate)
	{}
};
//在这里重载商品的大于和小于是不好的.
//因为只能解决一种比较方式,只能说按某一项去比
//通过之前的学习,仿函数的比较是最好的
//按照价格的小于比较
struct ComparePriceLess
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price < g2._price;
	}
};
//按价格的大于比较
struct ComparePriceGreater
{
	bool operator()(const Goods& g1, const Goods& g2)
	{
		return g1._price > g2._price;
	}
};

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};

using placeholders::_1;
using placeholders::_2;
using placeholders::_3;

int Sub(int a, int b)
{
	return (a - b) * 10;
}
int SubX(int a, int b,int c)
{
	return (a - b - c) * 10;
}
int main()
{
	auto sub1 = bind(Sub, _1, _2);
	//_1始终代表第一个参数,_2始终代表第二个参数
	cout << sub1(10, 5) << endl;

	//bind本质返回了一个仿函数对象,bind本身是函数模板
	//调整参数的顺序(不常用)
	auto sub2 = bind(Sub, _2, _1);
	cout << sub2(10, 5) << endl;
	
	//调整参数个数(常用) -将第一个参数绑死
	auto sub3 = bind(Sub, 100, _1);
	//_1代表第一个实参,这里只有一个实参
	cout << sub3(5) << endl;

	//调整参数个数 -将第二个参数绑死
	auto sub4 = bind(Sub, _1,100);
	//_1代表第一个实参,这里只有一个实参
	cout << sub4(5) << endl;

	//调整参数个数 - 将第一个参数绑死
	auto sub5 = bind(SubX, 100, _1, _2);
	//_1代表第一个参数,_2代表第二个参数
	cout << sub5(5, 1) << endl;

	//调整参数个数 - 将中间的参数绑死
	auto sub6 = bind(SubX, _1, 100, _2);
	cout << sub6(5, 1) << endl;

	//调整参数个数 - 将第三个参数绑死
	auto sub7 = bind(SubX, _1, _2, 100);
	cout << sub7(5, 1) << endl;
	//底层就是bind返回一个仿函数对象,这个对象调用operator(),
	//调用operator()里面又去调用SubX,

	//其实真正是这样用的 - 每次调用都要传有名对象或者匿名对象
	//确实有点烦,所以我们这里就可以绑定一下
	function<double(Plus, double, double)> f6 = &Plus::plusd;
	Plus pd;
	cout << f6(pd, 1.2, 2.1) << endl;
	cout << f6(Plus(), 1.2, 2.1) << endl;

	//bind一般用于,绑死一些固定参数
	function<double(double, double)> f7 = bind(&Plus::plusd,Plus(),_1,_2);
	cout << f7(1.2, 2.1) << endl;


	//计算利率
	//计算单率
	//auto func1 = [](double rate, double monty, int year)->double {return monty * rate * year; };
	//计算复率
	auto func1 = [](double rate, double monty, int year)->double 
	{
		double ret = monty;
		for (int i = 0;i < year;i++)
		{
			//上一年的结果*=利率然后再加上上一年的结果
			ret += ret * rate;
		}
		return ret - monty;
	};
	function<double(double)> func3_1_5 = bind(func1,0.015,_1 ,3);
	function<double(double)> func5_1_5 = bind(func1,0.015,_1 ,5);
	function<double(double)> func10_2_5 = bind(func1,0.025,_1 ,10);
	function<double(double)> func20_3_5 = bind(func1,0.035,_1 ,20);

	cout << func3_1_5(1000000) << endl;
	cout << func5_1_5(1000000) << endl;
	cout << func10_2_5(1000000) << endl;
	cout << func20_3_5(1000000) << endl;

	return 0;
}



////函数指针
//int f(int a, int b)
//{
//	return a + b;
//}
//
////仿函数
//struct Functor
//{
//public:
//	int operator()(int a, int b)
//	{
//		return a + b;
//	}
//};
//
//class Plus
//{
//public:
//	static int plusi(int a, int b)
//	{
//		return a + b;
//	}
//	double plusd(double a, double b)
//	{
//		return a + b;
//	}
//};
//
//int main()
//{
//	//包装可调用对象
//	//包装函数
//	function<int(int, int)> f1 = f;
//	//包装函数对象
//	function<int(int, int)> f2 = Functor();
//	//包装lambda
//	function<int(int, int)> f3 = [](int a, int b) {return a + b; };
//	
//	cout << f1(1, 2) << endl;
//	cout << f2(1, 2) << endl;
//	cout << f3(1, 2) << endl;
//	
//	//包装静态成员函数
//	//包装类里面的静态成员函数跟普通的没什么区别
//	//但是成员函数的函数名受类域的限制,所以得指定类域
//	//这里可以不加&,但是建议加上
//	function<int(int, int)> f4 = &Plus::plusi;
//	cout << f4(1, 2) << endl;
//
//	//包装普通成员函数
//	//function<double(double, double)> f5 = Plus::plusd;
//	//这样写编不过
//
//	//function<double(double, double)> f5 = &Plus::plusd;
//	//非静态的成员函数取函数指针的时候要加个&,可以认为是语法规定
//	//静态的可以不加,但是统一都加上
//	
//	function<double(Plus*,double, double)> f5 = &Plus::plusd;
//	//加上&还是会报错的,因为包装的类型对不上,因为这里的Plus定义
//	//还有一个隐含的this,所以我们还得增加一个参数
//	Plus pd;
//	cout << f5(&pd,1.2, 2.1) << endl;
//
//	//当然,不传指针也可以,就传对象
//	function<double(Plus, double, double)> f6 = &Plus::plusd;
//	Plus pd;
//	//传指针的话就可以考虑传有名对象,也可以传匿名对象
//	cout << f6(pd, 1.2, 2.1) << endl;
//	cout << f6(Plus(), 1.2, 2.1) << endl;
//
//	//第一个参数是隐含的this,应该是Plus*,为什么Plus也可以呢?
//	//function的本质是把这几个参数直接传递给plusd吗?
//	//其实不是直接传给plusd的,它是接收到这个函数指针之后,里面类似
//	//成员变量的方式存起来,存起来了以后我调用的时候调用的是operator()
//	//它的operatpr括号取调用可调用的对象,是lambda就反向掉lambda,也就是
//	//说再这里调operator(),operator中又去调用plusd这个函数,你是一个指针
//	//我可以调用这个函数.你是一个对象,我也可以调用这个函数。
//	//也就是说这三个参数不是直接传给plusd的,况且this指针不支持显示的传
//	//底层就是用指针或者对象去调用这个plusd这个函数,所以说指针可以,对象
//	//也可以,它不是直接传,this指针不支持显示传递,它是转换成对应的调用 
//	return 0;
//}


//也可以支持按评价的大于小于比较
//那么这里就支持不同的比较方式,但是写起来也比较麻烦.所以
//C++就支持了lambda
//int main()
//{
//	vector<Goods> v = { {"苹果",2.1,5},{"香蕉",3,4},{"橙子",2.2,3} };
//	sort(v.begin(), v.end(), ComparePriceLess());//传匿名对象,而不是类型
//	sort(v.begin(), v.end(), ComparePriceGreater());
//	//价格的升序
//	sort(v.begin(), v.end(), [](const Goods& g1,const Goods& g2)->bool
//		{
//			return g1._price < g2._price;
//		});
//	//价格的降序
//	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool
//		{
//			return g1._price > g2._price;
//		});
//	//评价的升序
//	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool
//		{
//			return g1._enaluate < g2._enaluate;
//		});
//	//评价的降序
//	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2)->bool
//		{
//			return g1._enaluate > g2._enaluate;
//		});
//	return 0;
//}

//int main()
//{
//	int a = 1, b = 0;
//	auto swap1 = [](int& x, int& y)
//	{
//		//只能用当前lambda局部域的东西和捕捉的对象
//		int tmp = x;
//		x = y;
//		y = tmp;
//	};//lambda单独写的时候后面要加分号
//	swap1(a, b);
//	//专门交换a和b的lambda,这个时候就要用捕捉列表了,捕捉谁就写谁
//	//传值捕捉本质是一种拷贝,并且用const修饰了
//	//mutable相当于去掉了const属性,可以修改了
//	//但是修改了不会影响外面被捕捉的值,因为是一种拷贝
//	auto swap2 = [a, b] ()mutable
//	{
//		int tmp = a;
//		a = b;
//		b = tmp;
//	};
//	swap2();
//	//这里的话我们就必须得传引用了 - 引用捕捉
//	auto swap3 = [&a, &b]
//	{
//		int tmp = a;
//		a = b;
//		b = tmp;
//	};
//	swap3();
//	return 0;
//}

//int main()
//{
//	int a = 0, b = 1, c = 2, d = 3;
//	//所有的值都以传值的方式捕捉
//	auto func1 = [=]
//	{
//		int ret = a + b + c + d;
//		return ret;
//	};
//	//所有的值都以引用的方式捕捉
//	auto func2 = [&]
//	{
//		a++;
//		b++;
//		c++;
//		d++;
//		int ret = a + b + c + d;
//		return ret;
//	};
//	//混合捕捉
//	auto func3 = [&a, b]
//	{
//		a++;
//		//b++;
//		int ret = a + b;
//		return ret;
//	};
//	//混合捕捉,全部都以引用的方式捕捉,但是d以传值的方式捕捉
//	auto func4 = [&, d]
//	{
//		a++;
//		b++;
//		c++;
//		//d++;
//		int ret = a + b + c + d;
//		return ret;
//	};
//	//混合捕捉,全部都以传值的方式捕捉,但是d以传引用的方式捕捉
//	auto func5 = [=, &d]
//	{
//		//a++;
//		//b++;
//		//c++;
//		d++;
//		int ret = a + b + c + d;
//		return ret;
//	};
//	return 0;
//}

//int x = 10;
//int main()
//{
//	//只能用当前lambda局部域的东西和捕捉对象, 当然全局的也可以用。
//	int a = 0, b = 1;
//	auto func1 = [&a, &b]
//	{
//		a++;
//		b++;
//		x++;
//		int ret = a + b + x;
//		return ret;
//	};
//	cout << func1() << endl;
//	return 0;
//}

//class Rate
//{
//public:
//	Rate(double rate) : _rate(rate)
//	{}
//	double operator()(double money, int year)
//	{
//		return money * _rate * year;
//	}
//private:
//	double _rate;
//};
//
////lambda 匿名函数对象
//int main()
//{
//	//函数对象
//	double rate = 0.015;
//	Rate r1(rate);
//	cout << r1(10000, 2) << endl;
//	//lambda
//	auto r2 = [rate](double monty, int year)->double
//	{
//		return monty * rate * year; 
//	};
//	cout << r2(10000, 2) << endl;
//	//lambda
//	/*int x = 0, y = 0;
//	auto r3 = [=](double monty, int year)->double
//	{
//		return monty * rate * year;
//	};
//	cout << r3(10000, 2) << endl;*/
//	//lambda生成仿函数的类的名称叫lambda + uuid
//	//r2和r3的lambda是完全一样的,里面的内容也是完全一样的
//	//但是它们是不同的类型,所以它们之间不能互相赋值
//	return 0;
//}


//两个数相加的lambda
//int main()
//{
//	//lambda对象
//	//局部匿名函数
//	//这里只能用auto,让它自动推导
//	auto add1 = [](int x, int y)->int {return x + y; };
//	//跟函数调用的方法是一样的
//	cout << add1(1, 8) << endl;
//	
//	//hello world
//	auto func1 = []()->int 
//	{
//		cout << "hello world" << endl;
//		return 0;
//	};
//	cout << func1() << endl;
//	//如果是无参的,可以省略参数列表,这里我们明确了返回类型,所以
//	//返回类型我们可以不用写,没有返回值可以自动推导,有返回值它也
//	//可以自动推导 
//	auto func2 = []
//	{
//		cout << "hello wang" << endl;
//	};
//	func2();
//	return 0;
//}
